package com.sql.common.util;

import com.lcx.util.ReflectUtil;
import com.me.common.ToolsList;
import com.sql.common.Dao;
import com.sql.common.DaoImpl;

import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Connection;
import java.sql.Driver;
import java.util.Properties;

/**
 * Created by Shisan on 2017/11/24.
 */
public class ConnectionFactory {

    private Connection connection;

    private Dao proxyDao;

    private String userName;

    private Class<?> driveClass;

    private Class<?> jschClass;

    private Object jsch;

    private Object session;

    public static final String CONNECTION_FILE_NAME = "jdbc.driver.jarfile";
    public static final String CONNECTION_DRIVER_CLASS_NAME = "jdbc.driver.driverClass";
    public static final String CONNECTION_USER_NAME = "jdbc.driver.user";
    public static final String CONNECTION_PASSWORD_NAME = "jdbc.driver.key";
    public static final String CONNECTION_URL_NAME = "jdbc.driver.url";
    public static final String CONNECTION_SSH_HOST_NAME="ssh.host";
    public static final String CONNECTION_SSH_PORT_NAME="ssh.port";
    public static final String CONNECTION_SSH_USER_NAME="ssh.user";
    public static final String CONNECTION_SSH_PASSWORD_NAME="ssh.password";
    public static final String CONNECTION_PROXY_HOST_NAME="proxy.host";
    public static final String CONNECTION_PROXY_PORT_NAME="proxy.port";
    public static final String CONNECTION_TARGET_HOST_NAME="target.host";
    public static final String CONNECTION_TARGET_PORT_NAME="target.port";


    public Connection getConnection(PropertyUtil propertyUtil){

        if(connection == null){
            synchronized (ConnectionFactory.class){
                if(connection == null){
                    try{
                        if(driveClass == null){
                            File file = new File(propertyUtil.getProperty(CONNECTION_FILE_NAME));
                            if(!file.exists()){
                                throw new Exception("数据库驱动包不存在！");
                            }
                            ClassLoader loader = URLClassLoader.newInstance(new URL[]{new URL("file:///"+file.getAbsolutePath().replaceAll("\\\\","/"))});
                            Thread.currentThread().setContextClassLoader(loader);
                            driveClass = loader.loadClass(propertyUtil.getProperty(CONNECTION_DRIVER_CLASS_NAME));
                        }

                        String sshHost = propertyUtil.getProperty(CONNECTION_SSH_HOST_NAME);
                        if(sshHost!=null && !sshHost.trim().isEmpty()){
                            if(jsch==null){
                                File file = new File(ToolsList.PLUG_LIB_DIR+"/jsch.jar");
                                if(!file.exists()){
                                    throw new Exception("SSH 驱动不存在！");
                                }
                                ClassLoader loader = URLClassLoader.newInstance(new URL[]{new URL("file:///"+file.getAbsolutePath().replaceAll("\\\\","/"))});
                                jschClass = loader.loadClass("com.jcraft.jsch.JSch");
                                jsch = jschClass.newInstance();
                            }
                            session = ReflectUtil.invokeMethod(jschClass,jsch,"getSession",new Class<?>[]{String.class,String.class,int.class,},propertyUtil.getProperty(CONNECTION_SSH_USER_NAME), sshHost, Integer.parseInt(propertyUtil.getProperty(CONNECTION_SSH_PORT_NAME)));
                            ReflectUtil.invokeMethod(session.getClass(),session,"setPassword",new Class[]{String.class},propertyUtil.getProperty(CONNECTION_SSH_PASSWORD_NAME));
                            ReflectUtil.invokeMethod(session.getClass(),session,"setConfig",new Class[]{String.class,String.class},"StrictHostKeyChecking", "no");
                            ReflectUtil.invokeMethod(session.getClass(),session,"setTimeout",new Class[]{int.class},8000);
                            ReflectUtil.invokeMethod(session.getClass(),session,"connect",null);
                            ReflectUtil.invokeMethod(session.getClass(),session,"setPortForwardingL",new Class[]{String.class,int.class,String.class,int.class},propertyUtil.getProperty(CONNECTION_PROXY_HOST_NAME),Integer.parseInt(propertyUtil.getProperty(CONNECTION_PROXY_PORT_NAME)),propertyUtil.getProperty(CONNECTION_TARGET_HOST_NAME),Integer.parseInt(propertyUtil.getProperty(CONNECTION_TARGET_PORT_NAME)));
                        }
                        Driver driver = (Driver) driveClass.getDeclaredConstructor().newInstance();
                        Properties p = new Properties();
                        userName=propertyUtil.getProperty(CONNECTION_USER_NAME);
                        if(userName != null && !userName.isEmpty()){
                            p.put("user",userName);
                            String password = propertyUtil.getProperty(CONNECTION_PASSWORD_NAME);
                            if(password != null && !password.isEmpty()){
                                p.put("password",password);
                            }
                        }
                        p.put("remarks","true");
                        connection = driver.connect(propertyUtil.getProperty(CONNECTION_URL_NAME),p);
                        connection.setAutoCommit(false);
                        final DaoImpl daoImpl = new DaoImpl(connection);
                        proxyDao = (Dao) Proxy.newProxyInstance(DaoImpl.class.getClassLoader(), new Class[]{Dao.class}, new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                return method.invoke(daoImpl,args);
                            }
                        });
                    }catch(Exception e){
                        try{
                            connection.close();
                        }catch (Exception e1){}
                        try{
                            ReflectUtil.invokeMethod(session.getClass(),session,"disconnect",null);
                        }catch (Exception e1){}
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return connection;
    }

    public Dao getDao(){
        return proxyDao;
    }

    public void commit() throws Exception {
        connection.commit();
    }

    public void rollback() throws Exception {
        connection.rollback();
    }

    public String getUserName(){
        return userName;
    }
    public String getUserNameUpper(){
        return userName==null?null:userName.toUpperCase();
    }

    public void close(){
        try{
            connection.close();
        }catch (Exception e){}
        finally {
            connection = null;
        }
        try{
            ReflectUtil.invokeMethod(session.getClass(),session,"disconnect",null);
        }catch (Exception e){}
        finally {
            session = null;
        }
    }

}
